package com.wang.lottery.application.process.impl;

import com.wang.lottery.application.mp.producer.KafkaProducer;
import com.wang.lottery.application.process.IActivityProcess;
import com.wang.lottery.application.process.req.DrawProcessReq;
import com.wang.lottery.application.process.res.DrawProcessResult;
import com.wang.lottery.application.process.res.RuleQuantificationCrowdResult;
import com.wang.lottery.common.Constants;
import com.wang.lottery.common.Result;
import com.wang.lottery.domain.activity.model.req.PartakeReq;
import com.wang.lottery.domain.activity.model.res.PartakeResult;
import com.wang.lottery.domain.activity.model.vo.ActivityPartakeRecordVO;
import com.wang.lottery.domain.activity.model.vo.InvoiceVO;
import com.wang.lottery.domain.activity.service.partake.IActivityPartake;
import com.wang.lottery.domain.award.model.vo.DrawOrderVO;
import com.wang.lottery.domain.rule.model.req.DecisionMatterReq;
import com.wang.lottery.domain.rule.model.res.EngineResult;
import com.wang.lottery.domain.rule.service.engine.EngineFilter;
import com.wang.lottery.domain.strategy.model.req.DrawReq;
import com.wang.lottery.domain.strategy.model.res.DrawResult;
import com.wang.lottery.domain.strategy.model.vo.DrawAwardInfo;
import com.wang.lottery.domain.strategy.service.draw.IDrawExec;
import com.wang.lottery.domain.support.ids.IIdGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.support.SendResult;
import org.springframework.stereotype.Service;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;

/**
 * @author wjh
 * @create 2023-11-09 14:18
 * 活动抽奖流程编排实现类
 */
@Service
@Slf4j
public class ActivityProcessImpl implements IActivityProcess {
    
    @Resource
    private IActivityPartake activityPartake;
    @Resource
    private IDrawExec drawExec;
    @Resource
    private Map<Constants.Ids, IIdGenerator> idGeneratorMap;
    @Resource
    private EngineFilter engineFilter;
    @Resource
    private KafkaProducer kafkaProducer;
    
    /**
     * 开始抽奖流程
     *
     * @param req 抽奖流程
     * @return 抽奖结果
     */
    @Override
    public DrawProcessResult doDrawProcess(DrawProcessReq req) {
        PartakeReq partakeReq = new PartakeReq(req.getuId(), req.getActivityId());
        partakeReq.setPartakeDate(new Date());
        //领取活动
        PartakeResult partakeResult = activityPartake.doPartake(partakeReq);
        if (!Constants.ResponseCode.SUCCESS.getCode().equals(partakeResult.getCode())) {
            log.error("活动参与失败，原因：{}", partakeResult.getInfo());
            return new DrawProcessResult(partakeResult.getCode(),partakeResult.getInfo());
        }
        if (partakeResult.getCode().equals(Constants.ResponseCode.SUCCESS.getCode()) && partakeResult.getStockSurplusCount() != null) {
            ActivityPartakeRecordVO activityPartakeRecordVO = new ActivityPartakeRecordVO();
            activityPartakeRecordVO.setActivityId(req.getActivityId());
            activityPartakeRecordVO.setStockSurplusCount(partakeResult.getStockSurplusCount());
            kafkaProducer.sendLotteryActivityPartakeRecord(activityPartakeRecordVO);
        }
        //封装抽奖请求参数
        Long strategyId = partakeResult.getStrategyId();
        DrawReq drawReq = new DrawReq(req.getuId(), strategyId);
        //进行抽奖
        DrawResult drawResult = drawExec.doDrawExec(drawReq);
        if (Constants.DrawState.FAIL.getCode().equals(drawResult.getDrawStatus())) {
            log.error("未中奖，原因：{}", drawResult.getDrawAwardInfo());
            return new DrawProcessResult(Constants.ResponseCode.LOSING_DRAW.getCode(), Constants.ResponseCode.LOSING_DRAW.getInfo());
        }
        //获取抽奖信息结果
        DrawAwardInfo drawAwardInfo = drawResult.getDrawAwardInfo();
        
        //结果落库
        DrawOrderVO drawOrderVO = buildDrawOrderVO(req, strategyId, partakeResult.getTakeId(), drawAwardInfo);
        Result result = activityPartake.recordDrawOrder(drawOrderVO);
        if (!Constants.ResponseCode.SUCCESS.getCode().equals(result.getCode())) {
            log.error("结果落库失败，原因：{}", result.getInfo());
            return new DrawProcessResult(result.getCode(), result.getInfo());
        }
        //发送MQ，触发发奖流程
        InvoiceVO invoiceVO = buildInvoiceVO(drawOrderVO);
        ListenableFuture<SendResult<String, Object>> listenableFuture = kafkaProducer.sendLotteryInvoice(invoiceVO);
        listenableFuture.addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {
            @Override
            public void onSuccess(SendResult<String, Object> result) {
                log.info("发奖MQ发送成功，结果：{}", result);
                // 4.1 MQ 消息发送完成，更新数据库表 user_strategy_export.mq_state = 1
                activityPartake.updateInvoiceMqState(invoiceVO.getuId(),invoiceVO.getOrderId(),Constants.MQState.COMPLETE.getCode());
            }
            @Override
            public void onFailure(Throwable ex) {
                // 4.2 MQ 消息发送失败，更新数据库表 user_strategy_export.mq_state = 2 【等待定时任务扫码补偿MQ消息】
                activityPartake.updateInvoiceMqState(invoiceVO.getuId(),invoiceVO.getOrderId(),Constants.MQState.FAIL.getCode());
            }
        });
        //封装抽奖结果
        DrawProcessResult drawProcessResult = new DrawProcessResult(Constants.ResponseCode.SUCCESS.getCode(), Constants.ResponseCode.SUCCESS.getInfo());
        drawProcessResult.setDrawAwardInfo(drawAwardInfo);
        return drawProcessResult;
    }

    private InvoiceVO buildInvoiceVO(DrawOrderVO drawOrderVO) {
        InvoiceVO invoiceVO = new InvoiceVO();
        invoiceVO.setuId(drawOrderVO.getuId());
        invoiceVO.setOrderId(drawOrderVO.getOrderId());
        invoiceVO.setAwardId(drawOrderVO.getAwardId());
        invoiceVO.setAwardType(drawOrderVO.getAwardType());
        invoiceVO.setAwardName(drawOrderVO.getAwardName());
        invoiceVO.setAwardContent(drawOrderVO.getAwardContent());
        invoiceVO.setShippingAddress(null);
        invoiceVO.setExtInfo(null);
        return invoiceVO;
    }

    /**
     * 规则量化人群，返回可参与的活动ID
     *
     * @param req 规则请求
     * @return 量化结果，用户可以参与的活动ID
     */
    @Override
    public RuleQuantificationCrowdResult doRuleQuantificationCrowd(DecisionMatterReq req) {
        EngineResult process = engineFilter.process(req);
        if (process != null && process.isSuccess()) {
            RuleQuantificationCrowdResult result = new RuleQuantificationCrowdResult(Constants.ResponseCode.SUCCESS.getCode(), Constants.ResponseCode.SUCCESS.getInfo());
            result.setActivityId(Long.valueOf(process.getNodeValue()));
            return result;
        }
        return new RuleQuantificationCrowdResult(Constants.ResponseCode.RULE_ERR.getCode(), Constants.ResponseCode.RULE_ERR.getInfo());
    }

    private DrawOrderVO buildDrawOrderVO(DrawProcessReq req, Long strategyId, Long takeId, DrawAwardInfo drawAwardInfo) {
        DrawOrderVO drawOrderVO = new DrawOrderVO();
        drawOrderVO.setuId(req.getuId());
        drawOrderVO.setActivityId(req.getActivityId());
        drawOrderVO.setTakeId(takeId);
        drawOrderVO.setStrategyId(strategyId);
        drawOrderVO.setOrderId(idGeneratorMap.get(Constants.Ids.SnowFlake).nextId());
        drawOrderVO.setStrategyMode(drawAwardInfo.getStrategyMode());
        drawOrderVO.setGrantType(drawAwardInfo.getGrantType());
        drawOrderVO.setGrantDate(drawAwardInfo.getGrantDate());
        drawOrderVO.setGrantState(Constants.GrantState.INIT.getCode());
        drawOrderVO.setAwardId(drawAwardInfo.getAwardId());
        drawOrderVO.setAwardType(drawAwardInfo.getAwardType());
        drawOrderVO.setAwardName(drawAwardInfo.getAwardName());
        drawOrderVO.setAwardContent(drawAwardInfo.getAwardContent());
        return drawOrderVO;
    }
}
